import time
from typing import Type, Any, Callable, Sequence, Optional, Iterable

from fastapi import Depends, Query, Body, Path
from fastapi.types import DecoratedCallable
from pydantic import create_model, BaseModel

from rosa.love_is_blue.backend.crud import CRUDGenerator, get_pk_type
from rosa.love_is_blue.snowflake import snow
from rosa.love_is_blue.types import BaseResponse, SiteException, DEPENDENCIES, GenerateRouterParams, PaginationData, GenericBaseResponse, RESPONSE_CALLABLE_LIST, PAGINATION, \
    PayloadData, signature_authentication, GetAllData, RESPONSE_CALLABLE, StatusMap, REAL_DELETE, ITEM_NOT_FOUND_RESPONSE, CREATE_FAILED_RESPONSE, UPDATE_FAILED_RESPONSE, \
    DELETE_FAILED_RESPONSE


class MemoryCRUDRouter(CRUDGenerator):
    # 需要严格认证的标识
    strict_authentication = False
    # 设置逻辑删除时，需要添加的后缀字段
    delete_prefix = "(deleted)"

    def __init__(
            self,
            schema: Type[BaseModel],
            create_schema: Type[BaseModel] | None = None,
            update_schema: Type[BaseModel] | None = None,
            prefix: str | None = None,
            tags: list[str] | None = None,
            paginate: int | None = None,
            get_all_route: bool | DEPENDENCIES = True,
            get_one_route: bool | DEPENDENCIES = True,
            create_route: bool | DEPENDENCIES = True,
            update_route: bool | DEPENDENCIES = True,
            delete_one_route: bool | DEPENDENCIES = True,
            delete_all_route: bool | DEPENDENCIES = False,
            get_all_route_params: GenerateRouterParams | None = None,
            get_one_route_params: GenerateRouterParams | None = None,
            create_route_params: GenerateRouterParams | None = None,
            update_route_params: GenerateRouterParams | None = None,
            delete_one_route_params: GenerateRouterParams | None = None,
            delete_all_route_params: GenerateRouterParams | None = None,
            verbose_name: str = '',
            verbose_name_plural: str = '',
            delete_update_field: str = '',
            memory: bool = True,
            primary_key: str = 'id',
            primary_key_type: type = None,
            strict_authentication: bool = False,
            **kwargs: Any
    ) -> None:
        if verbose_name:
            self.verbose_name = verbose_name
        else:
            self.verbose_name = schema.__name__.capitalize()
        if self.verbose_name and not verbose_name_plural:
            self.verbose_name_plural = f"{self.verbose_name}s"
        else:
            self.verbose_name_plural = verbose_name_plural
        self._primary_key = primary_key or 'id'
        self._primary_key_type = get_pk_type(schema, self._primary_key) if primary_key_type is None else primary_key_type
        # 逻辑删除时，需要设置的字段
        self.delete_update_field = delete_update_field

        # 获取所有数据和删除多个数据的模型进行存储，目前先定义，在init_router_params中进行初始化
        self.get_all_model = None
        self.delete_all_model = None
        # 初始化路由参数
        if get_all_route:
            get_all_route_params = self.create_route_params('Get All', get_all_route_params, schema, many=True)
            self.get_all_model = get_all_route_params.response_model
        if get_one_route:
            get_one_route_params = self.create_route_params('Get One', get_one_route_params, schema)
        if create_route:
            create_route_params = self.create_route_params('Create One', create_route_params, schema)
        if update_route:
            update_route_params = self.create_route_params('Update One', update_route_params, schema)
        if delete_one_route:
            delete_one_route_params = self.create_route_params('Delete One', delete_one_route_params, schema)
        if delete_all_route:
            delete_all_route_params = self.create_route_params('Delete All', delete_all_route_params, schema, many=True)
            self.delete_all_model = delete_all_route_params.response_model

        if memory:
            self.models: list[BaseModel] = []

        if strict_authentication:
            self.strict_authentication = strict_authentication

        super().__init__(
            schema=schema,
            create_schema=create_schema,
            update_schema=update_schema,
            prefix=prefix,
            tags=tags,
            paginate=paginate,
            get_all_route=get_all_route,
            get_one_route=get_one_route,
            create_route=create_route,
            update_route=update_route,
            delete_one_route=delete_one_route,
            delete_all_route=delete_all_route,
            get_all_route_params=get_all_route_params,
            get_one_route_params=get_one_route_params,
            create_route_params=create_route_params,
            update_route_params=update_route_params,
            delete_one_route_params=delete_one_route_params,
            delete_all_route_params=delete_all_route_params,
            **kwargs
        )

    def create_route_params(self, name: str, params: Optional[GenerateRouterParams] = None, schema: Optional[Type[BaseModel]] = None, many: bool = False) -> GenerateRouterParams:
        """
        生成创建路由的参数
        :param name: 路由名称前缀
        :param params: 本身的路由参数
        :param schema: 模型
        :param many: 是否为多个，多个时自动生成模型
        """
        if params:
            route_params = params
        else:
            route_params = GenerateRouterParams()

        if many:
            alias = self.verbose_name_plural
            # 如果是列表类型并且没有传入response_model，则自动生成一个模型
            if not route_params.response_model:
                route_params.response_model = create_model(
                    f'{self.verbose_name.title()}{name.replace(" ", "")}DataResponse',
                    items=(Optional[list[schema]], ...),
                    pagination=(PaginationData, ...)
                )
        else:
            alias = self.verbose_name
        route_params.summary = route_params.summary or f'{name} {alias}'
        return route_params

    # ##### 操作路由 #####
    def _add_api_route(
            self,
            path: str,
            endpoint: Callable[..., Any],
            dependencies: bool | DEPENDENCIES,
            methods: set[str] | list[str] | None = None,
            response_model: Sequence[Type[BaseModel]] | Type[BaseModel] | None = None,
            summary: str | None = None,
            description: str | None = None,
            responses: dict[int, dict[str, Any]] | None = None,
            **kwargs: Any
    ):
        # 如果没有传入response_model，则使用默认的Response，如果传入的response_model不是Response的子类，则使用GenericResponse
        if response_model is None:
            response_model = BaseResponse
        elif not (hasattr(response_model, 'success') and hasattr(response_model, 'code') and hasattr(response_model, 'message')):
            response_model = GenericBaseResponse[response_model]

        super()._add_api_route(path, endpoint, dependencies, methods, response_model, summary, description, responses, **kwargs)

    def api_route(
            self,
            path: str,
            methods: list[str] = None,
            response_model: Sequence[Type[BaseModel]] | Type[BaseModel] | None = None,
            *args,
            **kwargs) -> Callable[[DecoratedCallable], DecoratedCallable]:
        # 如果没有传入response_model，则使用默认的Response，如果传入的response_model不是Response的子类，则使用GenericResponse
        if response_model is None:
            response_model = BaseResponse
        elif not (hasattr(response_model, 'success') and hasattr(response_model, 'code') and hasattr(response_model, 'message')):
            response_model = GenericBaseResponse[response_model]

        return super().api_route(path, methods, response_model=response_model, *args, **kwargs)

    # ##### 完成路由 #####
    def _get_all(self, *args: Any, **kwargs: Any) -> RESPONSE_CALLABLE_LIST:
        async def route(
                pagination: PAGINATION = Depends(self.pagination),
                filters: list[str] = Query(
                    default=[],
                    title="filter params",
                    description="filter field and value",
                    example=["id=0"]
                ),
                orders: list[str] = Query(
                    default=[],
                    title="order params",
                    description="order field, only support forward order",
                    example=["id", "status"]
                ),
                ids: list[int] = Query(
                    default=[],
                    title="id params",
                    description="id list",
                    example=[1, 2, 3]
                ),
                payload: Optional[PayloadData] = Depends(signature_authentication) if self.strict_authentication else Depends(lambda: None)
        ) -> GenericBaseResponse[GetAllData]:
            return await self.get_all_handler(pagination=pagination, filters=filters, orders=orders, ids=ids, payload=payload)

        return route

    async def get_all_handler(
            self,
            pagination: PAGINATION,
            filters: list[str],
            orders: list[str],
            ids: list[int],
            payload: PayloadData | None = None
    ) -> GenericBaseResponse[GetAllData]:
        filters_dict = {}
        for f in filters:
            # 确保只有key和value
            if f.count("=") != 1:
                continue
            # 将空格全部进行替换，然后再进行分割
            f = f.replace(" ", "").split("=")
            # 如果是全新的key，则直接赋值，否则转换为列表
            if f[0] not in filters_dict:
                filters_dict[f[0]] = f[1]
            else:
                if isinstance(filters_dict[f[0]], list):
                    filters_dict[f[0]].append(f[1])
                else:
                    filters_dict[f[0]] = [filters_dict[f[0]], f[1]]
        if not orders:
            orders = ["-id"]

        all_records, count_records, pagination = await self.orm_get_all(pagination, filters_dict, orders, ids, payload)
        pagination_data = PaginationData(index=pagination.index, limit=pagination.limit, total=count_records, offset=pagination.offset)
        data = GetAllData(items=all_records, pagination=pagination_data).model_dump()
        response = GenericBaseResponse[GetAllData](data=data)

        return response

    def _create(self, *args: Any, **kwargs: Any) -> RESPONSE_CALLABLE:
        async def route(
                model: self.create_schema,  # type: ignore
                payload: Optional[PayloadData] = Depends(signature_authentication) if self.strict_authentication else Depends(lambda: None)
        ) -> BaseResponse:
            return await self.create_handler(model, payload)

        return route

    async def create_handler(self, model, payload: PayloadData | None = None) -> BaseResponse:
        """
        创建数据处理方法
        :param model: self.create_schema类型，创建数据的模型
        :param payload:
        :return:
        """
        model_dict = model.model_dump()
        create_model_dict = await self.orm_create(model_dict, dict(), payload)
        # 生成响应模型实例
        response = BaseResponse(data=create_model_dict)
        return response

    def _delete_all(self, *args: Any, **kwargs: Any) -> RESPONSE_CALLABLE_LIST:
        async def route(
                item_ids: list[self._primary_key_type] = Body(default=None, title="id list", description="delete item's id list", example=[1, 2, 3], ),  # type: ignore
                payload: Optional[PayloadData] = Depends(signature_authentication) if self.strict_authentication else Depends(lambda: None)
        ) -> GenericBaseResponse[GetAllData]:
            return await self.delete_all_handler(item_ids, payload)

        return route

    async def delete_all_handler(self, item_ids, payload: PayloadData | None = None) -> GenericBaseResponse[GetAllData]:
        """
        删除所有数据的处理方法
        :param item_ids: self._primary_key_type的列表类型，数据的id的集合
        :param payload:
        :return:
        """
        all_records, count_records, pagination = await self.orm_delete_all(item_ids, payload)

        pagination_data = PaginationData(index=pagination.index, limit=pagination.limit, total=count_records, offset=pagination.offset)
        data = GetAllData(items=all_records, pagination=pagination_data)
        return GenericBaseResponse[GetAllData](data=data)

    def _get_one(self, *args: Any, **kwargs: Any) -> RESPONSE_CALLABLE:
        async def route(
                item_id: self._primary_key_type = Path(..., alias="itemId"),  # type: ignore
                payload: Optional[PayloadData] = Depends(signature_authentication) if self.strict_authentication else Depends(lambda: None)
        ) -> BaseResponse:
            return await self.get_one_handler(item_id, payload)

        return route

    async def get_one_handler(self, item_id, payload: PayloadData | None = None) -> BaseResponse:
        """
        获取单个数据的处理方法
        :param item_id: self._primary_key_type类型，单个数据的id
        :param payload:
        :return:
        """
        model = await self.orm_get_one(item_id, payload)
        if model:
            response = BaseResponse(data=model)
            return response

        # 数据没有发现
        response = BaseResponse(
            code=ITEM_NOT_FOUND_RESPONSE.code,
            success=ITEM_NOT_FOUND_RESPONSE.success,
            message=ITEM_NOT_FOUND_RESPONSE.message
        )
        raise SiteException(status_code=StatusMap.ITEM_NOT_FOUND.http_code, response=response) from None

    def _update(self, *args: Any, **kwargs: Any) -> RESPONSE_CALLABLE:
        async def route(
                model: self.update_schema,  # type: ignore
                item_id: self._primary_key_type = Path(..., alias='itemId'),  # type: ignore
                payload: Optional[PayloadData] = Depends(signature_authentication) if self.strict_authentication else Depends(lambda: None)
        ) -> BaseResponse:
            return await self.update_handler(item_id, model, payload)

        return route

    async def update_handler(self, item_id, model, payload: PayloadData | None = None) -> BaseResponse:
        """
        更新数据的处理方法
        :param item_id: self._primary_key_type类型，单个数据的id
        :param model: self.update_schema类型，更新数据的模型
        :param payload:
        :return:
        """
        # 只获取更新后的数据
        model_dict = model.model_dump(exclude_unset=True, exclude={self._primary_key})
        update_model = await self.orm_update(item_id, model_dict, payload)
        if update_model:
            response = BaseResponse(data=update_model)
            return response
        response = BaseResponse(
            code=UPDATE_FAILED_RESPONSE.code,
            success=UPDATE_FAILED_RESPONSE.success,
            message=UPDATE_FAILED_RESPONSE.message
        )
        raise SiteException(status_code=StatusMap.ITEM_NOT_FOUND.http_code, response=response) from None

    def _delete_one(self, *args, **kwargs) -> RESPONSE_CALLABLE:
        async def route(
                item_id: self._primary_key_type = Path(..., alias="itemId"),  # type: ignore
                payload: Optional[PayloadData] = Depends(signature_authentication) if self.strict_authentication else Depends(lambda: None)
        ) -> BaseResponse:
            return await self.delete_one_handler(item_id, payload)

        return route

    async def delete_one_handler(self, item_id, payload: PayloadData | None = None) -> BaseResponse:
        """
        删除单个数据的处理方法
        :param item_id: self._primary_key_type类型，单个数据的id
        :param payload:
        :return:
        """
        delete_model = await self.orm_delete_one(item_id, payload)
        if delete_model:
            response = BaseResponse(data=delete_model)
            return response

        response = BaseResponse(
            code=DELETE_FAILED_RESPONSE.code,
            success=DELETE_FAILED_RESPONSE.success,
            message=DELETE_FAILED_RESPONSE.message
        )
        raise SiteException(status_code=StatusMap.ITEM_NOT_FOUND.http_code, response=response) from None

    # ###### 操作 ######
    async def orm_get_all(
            self,
            pagination: PAGINATION = None,
            filters: dict[str, str | list[str]] = None,
            orders: list[str] = None,
            ids: list[int] = None,
            payload: PayloadData | None = None
    ) -> tuple[list[BaseModel], int, PAGINATION]:
        """
        真正取数据的操作
        :param pagination: PAGINATION格式的页码数据
        :param filters: 字典格式，key为筛选的键，value为筛选的值
        :param orders: list类型，排序的字段名称
        :param ids: list类型，指定要刷选的多个数据id
        :param payload:
        :return: 返回一个元组，第一个元素为数据列表，第二个元素为数据总数，第三个元素为PAGINATION格式的页码数据
        """
        if pagination is None:
            pagination = self.pagination()
        if filters is None:
            filters = {}
        if orders is None:
            orders = ["-id"]

        count = 0
        count_records = 0
        all_records = []
        # 循环所有数据，如果符合条件则添加到 all_records 中
        for item in self.models:
            is_valid = True
            # 过滤条件目前只能匹配等于
            for filter_key in filters:
                item_filter_value = getattr(item, filter_key, None)
                if item_filter_value is None:
                    continue
                filter_value = filters[filter_key]
                if isinstance(filter_value, list):
                    if item_filter_value not in filter_value:
                        is_valid = False
                        break
                else:
                    if item_filter_value != filter_value:
                        is_valid = False
                        break
            if ids:
                if getattr(item, self._primary_key) not in ids:
                    is_valid = False

            if is_valid:
                count += 1
            # 如果符合条件的数据数量大于偏移量，则添加到 all_records 中
            if count > pagination.offset:
                all_records.append(item)
                count_records += 1
            # 数据取够了则退出循环
            if count_records >= pagination.offset + pagination.limit:
                break
        # 按照orders进行排序
        order_length = len(orders)
        if order_length > 0:
            order_key = orders[0]
            if order_key.startswith("-"):
                order_key = order_key[1:]
                all_records.sort(key=lambda x: getattr(x, order_key), reverse=True)
            else:
                all_records.sort(key=lambda x: getattr(x, order_key))
        return all_records, count_records, pagination

    async def orm_create(self, model_dict: dict[str, Any], other_data: dict[str, Any], payload: PayloadData | None = None) -> dict[str, Any]:
        """
        真正创建数据的操作
        :param model_dict: 创建的数据的dict类型
        :param other_data: 其他数据
        :param payload:
        :return: 创建之后数据的dict
        """
        model_dict["id"] = snow.get_id()
        ready_model = self.schema(**model_dict)
        self.models.append(ready_model)
        return model_dict

    async def orm_delete_all(self, item_ids: list[int], items: Iterable[BaseModel] = None, payload: PayloadData | None = None) -> tuple[list[BaseModel], int, PaginationData]:
        """
        真正删除所有数据的操作
        :param item_ids:
        :param items:
        :param payload:
        :return: 返回一个元组，第一个元素为数据列表，第二个元素为数据总数，第三个元素为PAGINATION格式的页码数据
        """
        all_records, count_records, pagination = await self.orm_get_all(pagination=None, filters=None, orders=None, ids=item_ids)
        records_data = list()
        for record in all_records:
            records_data.append(record.model_dump())

        # 如果是真删除，则直接删除，否则将名称添加后缀 {时间戳[-6:]}_delete 并将 status 设置为 inactive
        if REAL_DELETE:
            for row in all_records:
                self.models.remove(row)
        else:
            for row in all_records:
                values = dict()
                await self.set_delete_show_name(row, values)
                for key in values:
                    setattr(row, key, values[key])
        pagination = PaginationData(index=1, limit=count_records, total=count_records, offset=0)

        return all_records, count_records, pagination

    async def orm_get_one(self, item_id: int, payload: PayloadData | None = None) -> dict[str, Any] | None:
        """
        真正获取单个数据的操作
        :param item_id: 单个数据的id
        :param payload:
        :return: 返回单个数据的dict
        """
        for item in self.models:
            if getattr(item, self._primary_key) == item_id:
                data = item.model_dump()
                return data

        return None

    async def orm_update(self, item_id: int, model_dict: dict[str, Any], payload: PayloadData | None = None) -> dict[str, Any] | None:
        """
        真正更新数据的操作
        :param item_id: 操作数据的id
        :param model_dict: 更新的数据的dict
        :param payload:
        :return: 返回更新之后的数据的dict
        """
        for index, m in enumerate(self.models):
            if getattr(m, self._primary_key) == item_id:
                # 更新数据
                for key in model_dict:
                    setattr(m, key, model_dict[key])
                return model_dict
        return None

    async def orm_delete_one(self, item_id: int, payload: PayloadData | None = None) -> dict[str, Any] | None:
        """
        真正删除单个数据的操作
        :param item_id: 单个数据的id
        :param payload:
        :return: 返回删除之后的数据的dict
        """
        for m in self.models:
            if getattr(m, self._primary_key) == item_id:
                data = m.model_dump()
                if REAL_DELETE:
                    self.models.remove(m)
                else:
                    values = dict()
                    await self.set_delete_show_name(m, values)
                    for key in values:
                        setattr(m, key, values[key])
                return data

    # ###### 其他工具方法 ######
    async def get_delete_prefix(self):
        return self.delete_prefix

    async def set_delete_show_name(self, row, values):
        # 6位时间戳后缀，最小限度保证唯一性
        timestamp_last_six = str(int(time.time()))[-6:]
        prefix = f"{timestamp_last_six}{await self.get_delete_prefix()}"

        if isinstance(row, dict):
            if 'name' in row:
                values['name'] = f"{row['name']}_{prefix}"
            elif 'title' in row:
                values['title'] = f"{row['title']}_{prefix}"
            elif self.delete_update_field and self.delete_update_field in row:
                values[self.delete_update_field] = f"{row[self.delete_update_field]}_{prefix}"
        elif isinstance(row, BaseModel):
            if hasattr(row, 'name'):
                values['name'] = f"{row.name}_{prefix}"
            elif hasattr(row, 'title'):
                values['title'] = f"{row.title}_{prefix}"
            elif self.delete_update_field and hasattr(row, self.delete_update_field):
                values[self.delete_update_field] = f"{getattr(row, self.delete_update_field)}_{prefix}"

    def format_params(
            self,
            summary: str,
            description: str,
            params: dict | None = None,
            responses: dict | None = None,
            add_404: bool = False,
            add_key_exist: bool = False,
            add_create_fail: bool = False,
            add_update_fail: bool = False,
            add_delete_fail: bool = False,
    ) -> GenerateRouterParams:
        """
        通用方法，生成summary、description、responses
        :param summary:
        :param description:
        :param params:
        :param responses:
        :param add_404: 增加404响应模型, error code 264 没找到数据， error code 265 找到多条数据
        :param add_key_exist: 增加主键已存在响应模型，error code 266 主键已存在
        :param add_create_fail: 增加创建失败响应模型，error code 267 创建失败
        :param add_update_fail: 增加更新失败响应模型，error code 268 更新失败
        :param add_delete_fail: 增加删除失败响应模型，error code 269 删除失败
        :return:
        """
        router_params = super().format_params(summary, description, params, responses)

        if add_404:
            router_params.responses.update(ITEM_NOT_FOUND_RESPONSE)

        if add_create_fail:
            router_params.responses.update(CREATE_FAILED_RESPONSE)

        if add_update_fail:
            router_params.responses.update(UPDATE_FAILED_RESPONSE)

        if add_delete_fail:
            router_params.responses.update(DELETE_FAILED_RESPONSE)

        return router_params
